#include "platform/opengl/opengl_renderer_api.hpp"

namespace Xen {

void OpenGLRendererAPI::Init() {
    GLCall(glEnable(GL_BLEND));
    GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));

    GLCall(glEnable(GL_DEPTH_TEST));

    GLCall(glCullFace(GL_BACK));
    GLCall(glFrontFace(GL_CCW));
}

void OpenGLRendererAPI::Clear() {
    GLCall(glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT));
}

void OpenGLRendererAPI::ClearDepthBit() {
    GLCall(glClear(GL_DEPTH_BUFFER_BIT));
}

void OpenGLRendererAPI::ClearColorBit() {
    GLCall(glClear(GL_COLOR_BUFFER_BIT));
}

void OpenGLRendererAPI::ClearStencilBit() {
    GLCall(glClear(GL_STENCIL_BUFFER_BIT));
}

void OpenGLRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
    GLCall(glViewport(x, y, w, h));
}

glm::vec4 OpenGLRendererAPI::GetViewport() {
    int params[4] = {0};
    GLCall(glGetIntegerv(GL_VIEWPORT, params));
    return glm::vec4(params[0], params[1], params[2], params[3]);
}

void OpenGLRendererAPI::EnableCullFace() {
    GLCall(glEnable(GL_CULL_FACE));
}

void OpenGLRendererAPI::DisableCullFace() {
    GLCall(glDisable(GL_CULL_FACE));
}

void OpenGLRendererAPI::EnabelDepthTest() {
    GLCall(glEnable(GL_DEPTH_TEST));
}

void OpenGLRendererAPI::DisableDepthTest() {
    GLCall(glDisable(GL_DEPTH_TEST));
}

bool OpenGLRendererAPI::IsEnabelDepthTest() const {
    GLboolean b;
    GLCall(glGetBooleanv(GL_DEPTH_TEST, &b));
    return b;
}

void OpenGLRendererAPI::SetDepthFunc(DepthFunc func) {
    switch (func) {
        case DepthFunc::Always:
            GLCall(glDepthFunc(GL_ALWAYS));
            break;
        case DepthFunc::Never:
            GLCall(glDepthFunc(GL_NEVER));
            break;
        case DepthFunc::Less:
            GLCall(glDepthFunc(GL_LESS));
            break;
        case DepthFunc::Equal:
            GLCall(glDepthFunc(GL_EQUAL));
            break;
        case DepthFunc::LessEqual:
            GLCall(glDepthFunc(GL_LEQUAL));
            break;
        case DepthFunc::GeaterEqual:
            GLCall(glDepthFunc(GL_GEQUAL));
            break;
        case DepthFunc::NotEqual:
            GLCall(glDepthFunc(GL_NOTEQUAL));
            break;
        case DepthFunc::Greater:
            GLCall(glDepthFunc(GL_GREATER));
            break;
    }
}

void OpenGLRendererAPI::SetClearColor(const glm::vec4& color) {
    GLCall(glClearColor(color.r, color.g, color.b, color.a));
}

void OpenGLRendererAPI::DrawIndexed(const std::shared_ptr<VertexArray> &vertex_array) {
    GLCall(glDrawElements(GL_TRIANGLES, vertex_array->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, 0));
}

void OpenGLRendererAPI::DrawArrays(const std::shared_ptr<VertexArray>& vertex_array, uint32_t offset, uint32_t count) {
    GLCall(glDrawArrays(GL_TRIANGLES, offset, count));
}

}
